// ----------------------------------------------------------------------------
// atomic_counter.h
// ----------------------------------------------------------------------------
/*
 Description:
 排他制御つきカウンタ.

 ----------------------------------------------------------------
 使い方.
 AtomicCounter32(AtomicCounter64)クラスは32bit(64bit)の整数値を
 アトミックカウンタとして利用する為のラッパクラスです.
 このクラスのメソッドは不可分な処理として扱われます.
 
 // 例1. 単純なカウンタとして扱う場合. 
 fw::AtomicInt iCounter(0);
 int32_t index0 = iCounter++; // シングルスレッドで実行した場合、index0 = 0;
 int32_t index1 = ++iCounter; // シングルスレッドで実行した場合、index1 = 2;
 
 値をインクリメントするカウンタを複数のスレッドで共有する場合に、これらの操作が有用になります. 
 
 また、fw::AtomicCasPtrはポインタ値に対するCASを実現します.
 
 // 例2. ポインタのCASを行う. 
 int32_t  a=0, b=1234;
 int32_t* pa=&a;
 int32_t* pb=&b;
 
 if(fw::AtomicCasPtr(&pa, pa, pb)) // 第一引数が &a の形式になっていることに注意. 
 {
   std::cout << "*pa = " << *pa << std::endl; // 1234と表示されるべき. 
 }
 else
 {
   std::cout << "CAS failure." << std::endl;
 }
 
 */
#pragma once
#include <fw/base/concurrent/atomic_intrinsic.h>
namespace fw
{
	class AtomicCounter32
	{
	public:
		
		AtomicCounter32()
		: m_value( 0 )
		{
		}
		
		AtomicCounter32( int32_t value )
		: m_value( value )
		{
		}
		
		int32_t 						operator ++ ( int )
		{
			return FwAtomicIncrement_PreValue( &m_value );	/* 加算前の値を返す. */
		}
		
		int32_t 						operator -- ( int )
		{
			return FwAtomicDecrement_PreValue( &m_value );	/* 減算前の値を返す. */
		}
		
		int32_t 						operator ++ ()
		{
			return FwAtomicIncrement( &m_value );			/* 加算後の値を返す. */
		}
		
		int32_t 						operator -- ()
		{
			return FwAtomicDecrement( &m_value );			/* 減算後の値を返す. */
		}
		
		AtomicCounter32& 				operator += ( int32_t value )
		{
			FwAtomicAdd( &m_value, value );
			return *this;
		}
		
		AtomicCounter32& 				operator -= ( int32_t value )
		{
			FwAtomicAdd( &m_value, -value );
			return *this;
		}
		
		int32_t 						operator = ( int32_t exchange )
		{
			return FwAtomicSet( &m_value, exchange );
		}
		
		int32_t 						CompareExchange( int32_t comperand, int32_t exchange )
		{
			return FwAtomicCas( &m_value, comperand, exchange );
		}
		
		int32_t 						GetValue() const
		{
			return FwAtomicGet( &m_value );
		}
		
		operator						int32_t() const
		{
			return GetValue();
		}
		
	private:
		
		/*
		 Get時でも intrinsic を利用するのでmutableにする.
		 */
		mutable int32_t 				m_value;
		
	}; /* class AtomicCounter32 */
	
#if defined(FW_TARGET_64BIT)
	class AtomicCounter64
	{
	public:
		
		AtomicCounter64()
		: m_value( 0 )
		{
		}
		
		AtomicCounter64( int64_t value )
		: m_value( value )
		{
		}
		
		int64_t 						operator ++ ( int )
		{
			return FwAtomicIncrement_PreValue64( &m_value );	/* 加算前の値を返す. */
		}
		
		int64_t 						operator -- ( int )
		{
			return FwAtomicDecrement_PreValue64( &m_value );	/* 減算前の値を返す. */
		}
		
		int64_t 						operator ++ ()
		{
			return FwAtomicIncrement64( &m_value );				/* 加算後の値を返す. */
		}
		
		int64_t 						operator -- ()
		{
			return FwAtomicDecrement64( &m_value );				/* 減算後の値を返す. */
		}
		
		AtomicCounter64& 				operator += ( int64_t value )
		{
			FwAtomicAdd64( &m_value, value );
			return *this;
		}
		
		AtomicCounter64& 				operator -= ( int64_t value )
		{
			FwAtomicAdd64( &m_value, -value );
			return *this;
		}
		
		int64_t 						operator = ( int64_t exchange )
		{
			return FwAtomicSet64( &m_value, exchange );
		}
		
		int64_t 						CompareExchange( int64_t comperand, int64_t exchange )
		{
			return FwAtomicCas64( &m_value, comperand, exchange );
		}
		
		int64_t 						GetValue() const
		{
			return FwAtomicGet64( &m_value );
		}
		
		operator						int64_t() const
		{
			return GetValue();
		}
		
	private:
		
		/*
		 Get時でも intrinsic を利用するのでmutableにする.
		 */
		mutable int64_t 				m_value;
		
	}; /* class AtomicCounter64 */
#endif // FW_TARGET_64BIT 
	
	typedef AtomicCounter32 AtomicInt;
	
	// ポインタ値に対するCAS. 
	FW_FORCE_INLINE bool AtomicCasPtr( const void* const pos, const void* const oldval, const void* const newval )
	{
	#if defined(FW_TARGET_32BIT)
		return (FwAtomicCas  ((int32_t*)pos, (int32_t)oldval, (int32_t)newval) == (int32_t)oldval);
	#endif // FW_TARGET_32BIT
	#if defined(FW_TARGET_64BIT)
		return (FwAtomicCas64((int64_t*)pos, (int64_t)oldval, (int64_t)newval) == (int64_t)oldval);	
	#endif // FW_TARGET_64BIT
	}	
} // namespace fw 
